package kv

import (
	"database/sql"
	"gitee.com/tziye/renai-go/common"
	"gitee.com/tziye/renai-go/renai"
	"gitee.com/tziye/renai-go/sqlproxy"
)

type MapOperator struct {
	RenaiClient *renai.RenaiClient
	Db          *sql.DB
}

func NewMapOperator(renaiClient *renai.RenaiClient) *MapOperator {
	return &MapOperator{
		RenaiClient: renaiClient,
		Db:          renaiClient.Db,
	}
}

func (mapOperator *MapOperator) SaveKey(key string, expireSeconds int) {
	saveKey(mapOperator.Db, key, expireSeconds, Map)
}

func (mapOperator *MapOperator) Key(key string) RenaiKey {
	return queryKey(mapOperator.Db, key, Map)
}

func (mapOperator *MapOperator) Keys(keys []string) []RenaiKey {
	return queryKeys(mapOperator.Db, keys, Map)
}

func (mapOperator *MapOperator) AllKeys() []RenaiKey {
	return keysByType(mapOperator.Db, Map)
}

func (mapOperator *MapOperator) Like(pattern string) []RenaiKey {
	return keysLike(mapOperator.Db, pattern, Map)
}

func (mapOperator *MapOperator) Regexp(pattern string) []RenaiKey {
	return keysRegexp(mapOperator.Db, pattern, Map)
}

func (mapOperator *MapOperator) Values(key string) []RenaiValue {
	return values(mapOperator.Db, key, Map)
}

func (mapOperator *MapOperator) Total() int {
	return total(mapOperator.Db, Map)
}

func (mapOperator *MapOperator) Size(key string) int {
	return size(mapOperator.Db, key, Map)
}

func (mapOperator *MapOperator) Expire(key string, expireSeconds int) {
	expire(mapOperator.Db, key, expireSeconds, Map)
}

func (mapOperator *MapOperator) Exists(key string) bool {
	return exists(mapOperator.Db, key, Map)
}

func (mapOperator *MapOperator) Contains(key string, value any) bool {
	return contains(mapOperator.Db, key, value, Map)
}

func (mapOperator *MapOperator) Clear(key string) int {
	return clear(mapOperator.Db, key, Map)
}

func (mapOperator *MapOperator) Remove(key string, value any) bool {
	return remove(mapOperator.Db, key, value, Map)
}

func (mapOperator *MapOperator) BatchRemove(key string, values []any) int {
	return batchRemove(mapOperator.Db, key, values, Map)
}

func (mapOperator *MapOperator) Delete(key string) {
	delete(mapOperator.Db, key, Map)
}

func (mapOperator *MapOperator) BatchDelete(keys []string) {
	batchDelete(mapOperator.Db, keys, Map)
}

func (mapOperator *MapOperator) Flush() {
	flush(mapOperator.Db, Map)
}

func (mapOperator *MapOperator) Members(key string) map[string]string {
	renaiValues := values(mapOperator.Db, key, Map)
	values := make(map[string]string)
	for _, rv := range renaiValues {
		values[rv.SubK] = rv.V
	}
	return values
}

func (mapOperator *MapOperator) Put(key string, subKey string, value any) {
	tuples := make([]common.Tuple2, 0)
	tuples = append(tuples, common.NewTuple2(subKey, value))
	mapOperator.commonPut(key, tuples)
}

func (mapOperator *MapOperator) BatchPut(key string, values map[string]any) {
	tuples := make([]common.Tuple2, 0)
	for k, v := range values {
		tuples = append(tuples, common.NewTuple2(k, v))
	}
	mapOperator.commonPut(key, tuples)
}

func (mapOperator *MapOperator) commonPut(key string, values []common.Tuple2) {
	sqlproxy.CallRow(mapOperator.Db, common.KV_MAP_SET, key, common.String(values))
}

func (mapOperator *MapOperator) Get(key string, subKey string) string {
	rows := mapOperator.commonGet(key, []string{subKey}, 1)
	values := fetchSubKAndValue(rows)
	return values[subKey]
}

func (mapOperator *MapOperator) BatchGet(key string, subKeys []string) map[string]string {
	rows := mapOperator.commonGet(key, subKeys, 1)
	return fetchSubKAndValue(rows)
}

func (mapOperator *MapOperator) Pop(key string, subKey string) string {
	rows := mapOperator.commonGet(key, []string{subKey}, 2)
	values := fetchSubKAndValue(rows)
	return values[subKey]
}

func (mapOperator *MapOperator) BatchPop(key string, subKeys []string) map[string]string {
	rows := mapOperator.commonGet(key, subKeys, 2)
	return fetchSubKAndValue(rows)
}

func fetchSubKAndValue(rows *sql.Rows) map[string]string {
	defer rows.Close()
	values := make(map[string]string)
	for rows.Next() {
		var subK sql.NullString
		var value sql.NullString
		err := rows.Scan(&subK, &value)
		common.PanicErr(err)
		values[subK.String] = value.String
	}
	return values
}

func (mapOperator *MapOperator) removeKey(key string, subKey string) bool {
	rows := mapOperator.commonGet(key, []string{subKey}, 3)
	return fetchUpdated(rows) > 0
}

func (mapOperator *MapOperator) removeKeys(key string, subKeys []string) int {
	rows := mapOperator.commonGet(key, subKeys, 3)
	return fetchUpdated(rows)
}

func fetchUpdated(rows *sql.Rows) int {
	defer rows.Close()
	for rows.Next() {
		var updated sql.NullInt32
		err := rows.Scan(&updated)
		common.PanicErr(err)
		return int(updated.Int32)
	}
	return 0
}

func (mapOperator *MapOperator) commonGet(key string, subKeys []string, flag int) *sql.Rows {
	return sqlproxy.CallRows(mapOperator.Db, common.KV_MAP_GET, key, common.String(subKeys), flag)
}

func (mapOperator *MapOperator) ContainsKey(key string, subKey string) bool {
	s := "SELECT COUNT(id) FROM renai_kv_map_value_valid WHERE k = ? AND sub_k = ?"
	row := sqlproxy.QueryRow(mapOperator.Db, s, key, subKey)
	var count sql.NullInt32
	err := row.Scan(&count)
	common.PanicErr(err)
	return count.Int32 > 0
}

func (mapOperator *MapOperator) Incr(key string, subKey string, num int64) int64 {
	row := sqlproxy.CallRow(mapOperator.Db, common.KV_MAP_INCR, key, subKey, num)
	var count sql.NullInt64
	err := row.Scan(&count)
	common.PanicErr(err)
	return count.Int64
}
